#!/usr/bin/python
# -*- coding: utf-8 -*-

__author__="dmeziere"
__date__ ="$16 oct. 2011 20:40:03$"

try:
    import re
except ImportError:
    print "Impossible d'importer le module re. Est-il installé ?"
    exit(1)

try:
    import os
except ImportError:
    print "Impossible d'importer le module os. Est-il installé ?"
    exit(1)

try:
    import pycurl
except ImportError:
    print "Impossible d'importer le module pycurl. Est-il installé ?"
    exit(1)

class Storage:
    def __init__(self):
        self.contents = ''

    def store(self, buf):
        self.contents = "%s%s" % (self.contents, buf)

    def __str__(self):
        return self.contents

class FreeFax():

    id = None
    idt = None

    username = None
    password = None
    destinataire = None
    fichier = None
    masque = None
    rapport = None
    resolution = None

    def __init__(self, defaults = None):

        if defaults != None:

            if defaults.username != None:
                self.set_username(defaults.username)

            if defaults.password != None:
                self.set_password(defaults.password)

            if defaults.destinataire != None:
                self.set_destinataire(defaults.destinataire)

            if defaults.fichier != None:
                self.set_fichier(defaults.fichier)

            if defaults.masque != None:
                self.set_masque(defaults.masque)

            if defaults.rapport != None:
                self.set_rapport(defaults.rapport)

            if defaults.resolution != None:
                self.set_resolution(defaults.resolution)

    def set_id(self, chaine):
        """
        Définit l'id retourné par le site de Free après identification.

        Returns Bool
        """

        m = re.match('[0-9]+', chaine)

        if m == None:
            return False
        else:
            self.id = "%s" % chaine

        return True

    def get_id(self):
        """
        Retourne l'id Free.

        Returns String
        """
        return self.id

    def set_idt(self, chaine):
        """
        Définit l'idt retourné par le site de Free après identification.

        Returns Bool
        """

        m = re.match('[a-z0-9]+', chaine)

        if m == None:
            return False
        else:
            self.idt = "%s" % chaine

        return True

    def get_idt(self):
        """
        Retourne l'idt Free.

        Returns String
        """
        return self.idt

    def set_username(self, chaine):
        """
        Définit le nom d'utilisateur à partir de [chaine], si celle-ci est bien 
        constituée de 10 chiffres.

        Returns Bool
        """

        m = re.match('[0-9]{10}', chaine)

        if m == None:
            return False
        else:
            self.username = "%s" % chaine

        return True

    def get_username(self):
        """
        Retourne le nom d'utilisateur.

        Returns String
        """
        return self.username

    def set_password(self, chaine):
        """
        Définit le mot de passe à partir de [chaine], si celle-ci est bien
        constituée de chiffres et lettres en minuscule.

        Returns Bool
        """

        m = re.match('[a-z0-9]', chaine)

        if m == None:
            return False
        else:
            self.password = "%s" % chaine

        return True

    def get_password(self):
        """
        Retourne le mot de passe.

        Returns String
        """
        return self.password

    def set_destinataire(self, chaine):
        """
        Définit le numéro du destinataire à partir de [chaine], si celle-ci est 
        bien constituée de 10 chiffres. A noter quand dans les versions futures
        la restriction sur le format du numéro de téléphone devra être élargie
        dans la mesure ou l'envoit de fax chez Free suit les mêmes convenions 
        que les appels téléphoniques. On devra alors pouvoir utiliser un numéro 
        international comme destinataire.

        Returns Bool
        """

        m = re.match('[0-9]{10}', chaine)

        if m == None:
            return False
        else:
            self.destinataire = "%s" % chaine

        return True

    def get_destinataire(self):
        """
        Retourne le numéro du destinataire.

        Returns String
        """
        return self.destinataire

    def set_fichier(self, chaine):
        """
        Définit le fichier à envoyer à partir de [chaine], si celle-ci désigne 
        bien un fichier existant et accessible en lecture.
        Notes: Formats testés :
            - pdf
            - jpg
            - odt

        Returns Bool
        """

        chaine = "%s" % chaine

        test = os.access(chaine, os.R_OK)

        if test == False:
            return False
        else:
            self.fichier = chaine

        return True

    def get_fichier(self):
        """
        Retourne le chemin du fichier.

        Returns String
        """
        return self.fichier

    def set_masque(self, bool):
        """
        Masque le numéro d'expéditeur du fax si [bool] vaut True.

        Returns Bool
        """

        if bool:
            self.masque = 'Y'
        else:
            self.masque = ''

        return True

    def get_masque(self):
        """
        Retourne l'état du masquage du numéro expéditeur.

        Returns Bool
        """
        return self.masque

    def set_rapport(self, bool):
        """
        Envoit un rapport d'envoi du fax aux adresses email définies dans le 
        compte Free si [bool] vaut True (Voir la rubrique [Telephone] de 
        l'interface de gestion de votre compte Free).

        Returns Bool
        """

        if bool:
            self.rapport = '1'
        else:
            self.rapport = ''

        return True

    def get_rapport(self):
        """
        Retourne l'état du rapport d'envoi par email.

        Returns Bool
        """
        return self.rapport

    def set_resolution(self, bool):
        """
        Envoit le fax en haute résolution, si [bool] vaut True. Note : Cette
        fonction a été désactivée dans le formulaire d'envoi de fax du site,
        et son activation ne semble pas flagrante.
        
        Returns Bool
        """

        if bool:
            self.resolution = '1'
        else:
            self.resolution = ''

        return True

    def get_resolution(self):
        """
        Retourne l'état de l'envoi du fax en haute résolution.

        Returns Bool
        """
        return self.resolution

    def action_login(self, verbose = False):
        """
        Identifie le client cURL sur le site de Free avec les identifiants 
        fournis précédemment, et récupère l'id et l'idt qui serviront à la 
        navigation interne sur l'interface de gestion du compte. Si [verbose]
        vaut True, l'éventuelle raison d'échec est affichée sur la sortie
        standard.

        Returns Bool
        """

        if self.get_username() == '' or self.get_password() == '':
            if verbose:
                if self.get_username() == '':
                    print u"Identifiant non renseigné."
                if self.get_password() == '':
                    print u"Mot de passe non renseigné."
            return False

        retrieved_headers = Storage()

        post_vars = [
            ('link', ''),
            ('login', self.get_username()),
            ('pass', self.get_password()),
            ('ok', 'Connexion')
        ]

        curl = pycurl.Curl()
        curl.setopt(curl.POST, 1)
        curl.setopt(curl.URL, 'https://subscribe.free.fr/login/login.pl')
        curl.setopt(curl.HTTPPOST, post_vars)
        curl.setopt(curl.HEADERFUNCTION, retrieved_headers.store)

        try:
            curl.perform()
        except pycurl.error:
            if verbose:
                print u"Connexion impossible."
            return False

        if curl.getinfo(curl.HTTP_CODE) == 404:
            if verbose:
                print u"Erreur 404: Adresse invalide."
            return False

        elif curl.getinfo(curl.HTTP_CODE) == 500:
            if verbose:
                print u"Erreur 500: Serveur indisponible."
            return False

        curl.close()

        # Récupération du paramètre id dans la redirection
        m = re.search(u'(id=[0-9]+)', retrieved_headers.contents)
        if m == None:
            if verbose:
                print u"Erreur: Identifiant ou mot de passe invalide (id)."
            return False
        else:
            self.set_id(m.group(1)[3:])

        # Récupération du paramètre idt dans la redirection
        m = re.search(u'(idt=[0-9a-f]+)', retrieved_headers.contents)
        if m == None:
            if verbose:
                print u"Erreur: Identifiant ou mot de passe invalide (idt)."
            return False
        else:
            self.set_idt(m.group(1)[4:])

        return True

    def action_send(self, verbose = False):
        """
        Poste les informations et le fichier constituants le fax sur 
        l'interface de gestion du compte Free.

        Returns Bool
        """

        if self.get_id() == '' or self.get_idt() == '' or self.get_destinataire() == '' or self.get_fichier() == '':
            if verbose:
                if self.get_id() == '' or self.get_idt() == '':
                    print u"Pas de session active."
                if self.get_destinataire() == '':
                    print u"Numéro du destinataire absent."
                if self.get_fichier() == '':
                    print u"Fichier absent."
            return False

        curl = pycurl.Curl()

        post_vars = [
            ('id', self.get_id()),
            ('idt', self.get_idt()),
            ('destinataire', self.get_destinataire()),
            ('masque', self.get_masque() and 'Y'),
            ('email_ack', self.get_rapport() and '1'),
            ('high_res', self.get_resolution() and '1'),
            ('document', (curl.FORM_FILE, self.get_fichier()))
        ]

        curl.setopt(curl.POST, 1)
        curl.setopt(curl.URL,
                    "https://adsl.free.fr/admin/tel/fax/tel_ulfax.pl")
        curl.setopt(curl.HTTPPOST, post_vars)

        try:
            curl.perform()
        except pycurl.error:
            if verbose:
                print u"Connexion impossible."
            return False

        if curl.getinfo(curl.HTTP_CODE) == 404:
            if verbose:
                print u"Erreur 404: Adresse invalide."
            return False

        elif curl.getinfo(curl.HTTP_CODE) == 500:
            if verbose:
                print u"Erreur 500: Serveur indisponible."
            return False

        # Autres erreurs posssibles : "Vous envoyez trop de fax."
        # Ce message n'est pas lié au nombre de fax en boite de réception ou d'émission.
        # Se renseigner auprès de Free pour connaître les modalités, et accessoirement les formats supportés.
        # Cette limitation semble journalière. On a a nouveau la possibilité d'en envoyer le lendemain.
        
        curl.close()

        return True

    def action_logout(self, verbose = False):
        """
        Met fin à la session sur l'interface de gestion du compte Free.

        Returns Bool
        """

        if self.get_id() == '' or self.get_idt() == '':
            if verbose:
                print u"Pas de session active."
            return False

        curl = pycurl.Curl()
        curl.setopt(curl.URL,
                    'https://subscribe.free.fr/login/logout.pl?id=%s&idt=%s'
                    % ( self.get_id(), self.get_idt() ))

        try:
            curl.perform()
        except pycurl.error:
            if verbose:
                print u"Connexion impossible."
            return False

        if curl.getinfo(curl.HTTP_CODE) == 404:
            if verbose:
                print u"Erreur 404: Adresse invalide."
            return False

        elif curl.getinfo(curl.HTTP_CODE) == 500:
            if verbose:
                print u"Erreur 500: Serveur indisponible."
            return False

        curl.close()

        return True

    def action_fax(self, verbose = False):
        """
        Déclenche le login, l'envoi et le logout en un seul appel.

        Returns Bool
        """

        # Ouverture de la session Free.
        if self.action_login(verbose):
            if verbose:
                print "Login: OK"
        else:
            print "Login: KO"
            return False

        # Envoit du fax par la gestion du compte.
        if self.action_send(verbose):
            if verbose:
                print "Upload: OK"
        else:
            print "Upload: KO"
            self.action_logout(verbose)
            return False

        # Fermeture de la session Free.
        if self.action_logout(verbose):
            if verbose:
                print "Logout: OK"
        else:
            print "Logout: KO"
            return False

        return True
